package cn.vic.db;

import cn.vic.config.ConfigStatic;
import cn.vic.config.Configuration;
import cn.vic.config.model.DatabaseElement;
import cn.vic.exception.ApplicationException;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 简单的 数据库连接池
 * Created by vic on 2017/8/15.
 */
public class CloudPool {
    //private final ConcurrentLinkedQueue<Connection> connectionsPool = new ConcurrentLinkedQueue<>();
    private final LinkedBlockingDeque<Connection> connectionsPool = new LinkedBlockingDeque<>();

    //private final LinkedBlockingQueue<Connection> connectionsPool = new LinkedBlockingQueue<>();
    private int mixCount = 1;
    private int maxCount = 10;
    private static int delayLoadNum = 500;
    private int currentCount = 0;
    private DatabaseElement element;
    private String dateBase;
    private Configuration configuration;

    /**
     * 通过线程初始化N个连接
     *
     * @throws SQLException
     */
    public CloudPool(DatabaseElement element, String dateBase) {
        configuration = ConfigStatic.get("configuration", Configuration.class);
        this.element = element;
        this.dateBase = dateBase;
        this.mixCount = configuration.getConnectionPoolMixNum();
        this.maxCount = configuration.getConnectionPoolMaxNum();
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                init();
            }
        }, delayLoadNum);

    }

    private void init() {
        try {
            for (int i = currentCount; i < mixCount; i++) {
                free(this.createConnection());
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * 获取数据库连接
     *
     * @return
     */
    public synchronized Connection getConnection() throws ApplicationException, InterruptedException {
        if (!connectionsPool.isEmpty() || this.currentCount >= maxCount) {
            return connectionsPool.take();
        } else {
            return createConnection();
        }
    }

    /**
     * @param conn
     */
    public void free(Connection conn) {
        try {
            connectionsPool.put(conn);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭
     */
    public void close() throws SQLException {
        for (Connection connection : connectionsPool) {
            connection.close();
        }
        connectionsPool.clear();
    }

    /**
     * 如果一直在这里 创建线程 会超过最大的线程值
     *
     * @return
     * @throws SQLException
     */
    public Connection createConnection() throws ApplicationException {
        Connection realConn = element.connect(dateBase);
        ;
        realConn = new ConnectionHandler(this).bind(realConn);
        this.currentCount++;
        return realConn;
    }

    /**
     * 动态代理
     * 真正想关闭连接，无法关闭
     */
    class ConnectionHandler implements InvocationHandler {
        private Connection realConnection;
        private Connection warpedConnection;
        private CloudPool dataSource;

        ConnectionHandler(CloudPool dataSource) {
            this.dataSource = dataSource;
        }

        Connection bind(Connection realConn) {
            this.realConnection = realConn;
            this.warpedConnection = (Connection) Proxy.newProxyInstance(this
                    .getClass().getClassLoader(), new Class[]{Connection.class}, this);
            return warpedConnection;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            if ("isReadOnly".equals(method.getName())) {
                if (!this.warpedConnection.isClosed()) {
                    // this.dataSource.connectionsPool.add(this.warpedConnection);
                    this.dataSource.free(this.warpedConnection);
                }
            }
            return method.invoke(this.realConnection, args);
        }

    }
}
